package com.fuhailiu.opengl.widget;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLES30;

import com.fuhailiu.opengl.utils.GlUtil;
import com.fuhailiu.opengl.utils.LogUtil;
import com.fuhailiu.opengl.utils.ShaderUtil;
import com.fuhailiu.opengl.view.GLView;

public class GLVAO extends GLView {

	private final String TAG = getClass().getSimpleName();

	private final float x = 0.6f;
	private final float y = 0.3f;

	// 顶点坐标数据
	private float vertices[] = new float[] { 
			-x, -y, 0, // left
			x, -y, 0, // right
			0, y, 0, // top
	};

	// 顶点颜色数据
	private float colors[] = new float[] { 
			1, 0, 0, 1, // 红
			0, 1, 0, 1, // 绿
			0, 0, 1, 1, // 蓝
	};

	// 自定义渲染管线程序id
	private int handleProgram;
	// 顶点位置属性引用id
	private int handlePosition;
	// 顶点颜色属性引用id
	private int handleColor;

	public GLVAO(Context context) {
		super(context);
		initShader();
		initVaoAndVbo();
	}

	private static final String VERTEX_SHADER = 
			"attribute vec3 aPosition; //顶点位置\n" + 
			"attribute vec4 aColor; //顶点颜色\n" + 
			"varying vec4 vColor; //用于传递给片元着色器的变量\n" + 
			"void main()\n" + 
			"{\n" + 
			"	gl_Position = vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置\n" + 
			"	vColor = aColor; //将接收的颜色传递给片元着色器 \n" + 
			"}\n";

	private static final String FRAGMENT_SHADER = 
			"precision mediump float;\n" + 
			"varying vec4 vColor; //接收从顶点着色器过来的参数\n" + 
			"void main()\n" + 
			"{\n" + 
			"	gl_FragColor = vColor; //给此片元颜色值\n" + 
			"}\n";

	private void initShader() {
		LogUtil.LogD(TAG, "initShader   <---");

		// 基于顶点着色器与片元着色器创建程序
		handleProgram = ShaderUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
		// 获取程序中顶点位置属性引用id
		handlePosition = GLES30.glGetAttribLocation(handleProgram, "aPosition");
		// 获取程序中顶点颜色属性引用id
		handleColor = GLES30.glGetAttribLocation(handleProgram, "aColor");

		LogUtil.LogD(TAG, "initShader   --->");
	}

	private final int bufferCount = 2;
	private int vaoHandle;
	private int vboHandlePosition;
	private int vboHandleColor;

	private final int sizeOfVertexPosition = 3;
	private final int sizeOfVertexColor = 4;
	private final int vertexCount = 3;

	private void initVaoAndVbo() {
		LogUtil.LogD(TAG, "initVaoAndVbo   <---");

		// 创建VAO
		int[] vaoIds = new int[1];
		GLES30.glGenVertexArrays(1, vaoIds, 0);
		GlUtil.checkGlError("glGenVertexArrays");
		vaoHandle = vaoIds[0];

		// 绑定VAO
		GLES30.glBindVertexArray(vaoHandle);
		GlUtil.checkGlError("glBindVertexArray");

		// 创建VBO缓冲区对象
		int[] bufferIds = new int[bufferCount];
		GLES30.glGenBuffers(bufferCount, bufferIds, 0);
		GlUtil.checkGlError("glGenBuffers");

		vboHandlePosition = bufferIds[0];
		vboHandleColor = bufferIds[1];

		// 绑定顶点缓冲区
		GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandlePosition);
		GlUtil.checkGlError("glBindBuffer Position");
		// 加载顶点数据到缓冲区
		GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexCount * sizeOfVertexPosition * GlUtil.SIZE_OF_FLOAT, GlUtil.createFloatBuffer(vertices), GLES30.GL_STATIC_DRAW);
		GlUtil.checkGlError("glBufferData Position");
		// 启用顶点数组
		GLES30.glEnableVertexAttribArray(handlePosition);
		// 指定顶点数据
		GLES30.glVertexAttribPointer(handlePosition, sizeOfVertexPosition, GLES30.GL_FLOAT, false, sizeOfVertexPosition * GlUtil.SIZE_OF_FLOAT, 0);

		// 绑定颜色缓冲区
		GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandleColor);
		GlUtil.checkGlError("glBindBuffer Color");
		// 加载颜色数据到缓冲区
		GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexCount * sizeOfVertexColor * GlUtil.SIZE_OF_FLOAT, GlUtil.createFloatBuffer(colors), GLES30.GL_STATIC_DRAW);
		GlUtil.checkGlError("glBufferData Color");
		// 启用颜色数组
		GLES30.glEnableVertexAttribArray(handleColor);
		// 指定颜色数据
		GLES30.glVertexAttribPointer(handleColor, sizeOfVertexColor, GLES30.GL_FLOAT, false, sizeOfVertexColor * GlUtil.SIZE_OF_FLOAT, 0);

		// 解除VBO绑定
		GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
		// 解除VAO绑定
		GLES30.glBindVertexArray(0);

		LogUtil.LogD(TAG, "initVaoAndVbo   --->");
	}

	@Override
	public void release() {
		LogUtil.LogD(TAG, "release   <---");

		super.release();

		GLES30.glDeleteBuffers(bufferCount, new int[] { vboHandlePosition, vboHandleColor }, 0);
		GlUtil.checkGlError("glDeleteBuffers");

		GLES30.glDeleteVertexArrays(1, new int[] { vaoHandle }, 0);
		GlUtil.checkGlError("glDeleteVertexArrays");

		LogUtil.LogD(TAG, "release   --->");
	}

	public static final int BUFFER_SUB_DATA = 0;
	public static final int MAP_BUFFER_RANGE = 1;
	private int mUpdateMode = BUFFER_SUB_DATA;

	public void setUpdateMode(int mode) {
		LogUtil.LogD(TAG, "setUpdateMode   <---");
		LogUtil.LogD(TAG, "setUpdateMode   mode = " + mode);

		mUpdateMode = mode;

		LogUtil.LogD(TAG, "setUpdateMode   --->");
	}

	public void move(float transX, float transY) {
		vertices[6] += transX;
		vertices[7] += transY;
		vertices[8] = 0;

		int target = GLES30.GL_ARRAY_BUFFER;
		int offset = 2 * sizeOfVertexPosition * GlUtil.SIZE_OF_FLOAT;
		int size = 1 * sizeOfVertexPosition * GlUtil.SIZE_OF_FLOAT;
		float[] newVertices = new float[] { vertices[6], vertices[7], vertices[8] };

		// 绑定VAO
		GLES30.glBindVertexArray(vaoHandle);
		// 绑定VBO顶点缓冲区
		GLES30.glBindBuffer(target, vboHandlePosition);

		// 更新数据
		if (mUpdateMode == BUFFER_SUB_DATA) {
			GLES30.glBufferSubData(target, offset, size, GlUtil.createFloatBuffer(newVertices));
			GlUtil.checkGlError("glBufferSubData");
		} else {
			ByteBuffer buffer = (ByteBuffer) GLES30.glMapBufferRange(target, offset, size, GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);
			GlUtil.checkGlError("glMapBufferRange");
			FloatBuffer floatBuffer = buffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
			if (null != floatBuffer) {
				floatBuffer.put(newVertices).position(0);
			}
			GLES30.glUnmapBuffer(target);
		}

		// 解除绑定VBO
		GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
		// 解除绑定VAO
		GLES30.glBindVertexArray(0);
	}

	public void draw(GL10 gl) {
		// 使用shader程序
		GLES30.glUseProgram(handleProgram);

		// 绑定VAO
		GLES30.glBindVertexArray(vaoHandle);

		// 绘制三角形
		GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);

		// 解除绑定VAO
		GLES30.glBindVertexArray(0);

		// 禁用shader程序
		GLES30.glUseProgram(0);
	}

}
